﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using NUnit.Framework;
using WPFCABShell.Shell.Interface.Services.Settings;
using WPFCABShell.Shell.Interface.Services.Skinning;
using WPFCABShell.Shell.Services.Skinning;

namespace WPFCABShell.UnitTest.Shell.Services.Skinning
{
	[TestFixture]
	public sealed class SkinningServiceTest : UnitTest.Shared.UnitTest
	{
        private ISettingsService _settingsService;
		private SkinningService _skinningService;
		private SkinEventArgs _skinEventArgs;
		private SkinChangedEventArgs _skinChangedEventArgs;

		protected override void SetUpCore()
		{
			base.SetUpCore();

			ClearEvents();
            _settingsService = Mocks.DynamicMock<ISettingsService>();
			_skinningService = new SkinningService(_settingsService);

			_skinningService.SkinAdded += delegate(object sender, SkinEventArgs e)
			{
				_skinEventArgs = e;
			};

			_skinningService.SkinChanged += delegate(object sender, SkinChangedEventArgs e)
			{
				_skinChangedEventArgs = e;
			};
		}

		[Test]
		public void Default_ShouldBeEmpty()
		{
			Assert.IsFalse(_skinningService.Skins.GetEnumerator().MoveNext());
			Assert.IsNull(_skinningService.CurrentSkin);
		}

		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void Add_ShouldThrowIfNameIsNull()
		{
			_skinningService.Add(null, typeof(ResourceDictionary));
		}

		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void Add_ShouldThrowIfResourceTypeIsNull()
		{
			_skinningService.Add("name", null);
		}

		[Test]
		[ExpectedException(typeof(ArgumentException), ExpectedMessage = "resourcesType must be a ResourceDictionary.")]
		public void Add_ShouldThrowIfTypeIsntResourceDictionary()
		{
			_skinningService.Add("name", typeof(string));
		}

		[Test]
		[ExpectedException(typeof(ArgumentException), ExpectedMessage = "resourcesType must have a default constructor.")]
		public void Add_ShouldThrowIfResourcesTypeDoesntHaveDefaultConstructor()
		{
			_skinningService.Add("name", typeof(CustomResourceDictionary));
		}

		[Test]
		public void Add_ShouldAddSkin()
		{
			ISkin skin = _skinningService.Add("name", typeof(ResourceDictionary));
			Assert.IsNotNull(skin);
			Assert.AreEqual("name", skin.Name);
			Assert.AreSame(typeof(ResourceDictionary), skin.ResourcesType);
			IEnumerator<ISkin> enumerator = _skinningService.Skins.GetEnumerator();
			Assert.IsTrue(enumerator.MoveNext());
			Assert.AreSame(skin, enumerator.Current);
		}

		[Test]
		public void Add_ShouldRaiseSkinAdded()
		{
			Assert.IsNull(_skinEventArgs);
			ISkin skin = _skinningService.Add("name", typeof(ResourceDictionary));
			Assert.IsNotNull(_skinEventArgs);
			Assert.AreSame(skin, _skinEventArgs.Skin);
		}

		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void CurrentSkin_ShouldThrowIfNull()
		{
			_skinningService.CurrentSkin = null;
		}

		[Test]
		public void CurrentSkin_ShouldRaiseSkinChangedEvent()
		{
			ISkin firstSkin = _skinningService.Add("name", typeof(ResourceDictionary));

			Assert.IsNull(_skinChangedEventArgs);
			_skinningService.CurrentSkin = firstSkin;
			Assert.AreSame(firstSkin, _skinningService.CurrentSkin);
			Assert.IsNotNull(_skinChangedEventArgs);
			Assert.IsNull(_skinChangedEventArgs.OldSkin);
			Assert.AreSame(firstSkin, _skinChangedEventArgs.NewSkin);

			ISkin secondSkin = _skinningService.Add("second", typeof(ResourceDictionary));
			ClearEvents();

			Assert.IsNull(_skinChangedEventArgs);
			_skinningService.CurrentSkin = secondSkin;
			Assert.AreSame(secondSkin, _skinningService.CurrentSkin);
			Assert.IsNotNull(_skinChangedEventArgs);
			Assert.AreSame(firstSkin, _skinChangedEventArgs.OldSkin);
			Assert.AreSame(secondSkin, _skinChangedEventArgs.NewSkin);
		}

		#region Supporting Methods

		private void ClearEvents()
		{
			_skinEventArgs = null;
			_skinChangedEventArgs = null;
		}

		#endregion

		#region Supporting Types

		private sealed class CustomResourceDictionary : ResourceDictionary
		{
			public CustomResourceDictionary(int dummy)
			{
			}
		}

		#endregion
	}
}
